package org.abettor.angleworm.sip;

import java.net.InetAddress;
import java.net.UnknownHostException;
import java.text.ParseException;
import java.util.Properties;

import javax.sip.ClientTransaction;
import javax.sip.Dialog;
import javax.sip.DialogTerminatedEvent;
import javax.sip.IOExceptionEvent;
import javax.sip.InvalidArgumentException;
import javax.sip.ListeningPoint;
import javax.sip.RequestEvent;
import javax.sip.ResponseEvent;
import javax.sip.ServerTransaction;
import javax.sip.SipException;
import javax.sip.SipFactory;
import javax.sip.SipListener;
import javax.sip.SipProvider;
import javax.sip.SipStack;
import javax.sip.TimeoutEvent;
import javax.sip.TransactionTerminatedEvent;
import javax.sip.address.Address;
import javax.sip.address.AddressFactory;
import javax.sip.header.CSeqHeader;
import javax.sip.header.ContactHeader;
import javax.sip.header.ContentTypeHeader;
import javax.sip.header.HeaderFactory;
import javax.sip.message.MessageFactory;
import javax.sip.message.Request;
import javax.sip.message.Response;

public class SipCallee implements SipListener
{
	private static final String TRANSPORT = "udp";
	private static final int BIND_PORT = 13579;

	private SipStack sipStack;
	private MessageFactory messageFactory;
	private AddressFactory addressFactory;
	private HeaderFactory headerFactory;

	public void call() throws Exception
	{
		Properties prop = new Properties();
		prop.setProperty("javax.sip.IP_ADDRESS", InetAddress.getLocalHost().getHostAddress());
		prop.setProperty("javax.sip.RETRANSMISSION_FILTER", "true");
		prop.setProperty("javax.sip.STACK_NAME", "[org_abettor_angleworm_sip]");
		//	0为产品发布模式；16为跟踪模式；32为跟踪加调试模式
		prop.setProperty("gov.nist.javax.sip.TRACE_LEVEL", "32");
		prop.setProperty("gov.nist.javax.sip.DEBUG_LOG", "log/SipCallee.debug.log");
		prop.setProperty("gov.nist.javax.sip.SERVER_LOG", "log/SipCallee.log");
		prop.setProperty("gov.nist.javax.sip.READ_TIMEOUT", "100");
		prop.setProperty("gov.nist.javax.sip.CACHE_SERVER_CONNECTIONS", "false");

		SipFactory sipFactory = SipFactory.getInstance();
		sipFactory.setPathName("gov.nist");
		sipStack = sipFactory.createSipStack(prop);
		messageFactory = sipFactory.createMessageFactory();
		addressFactory = sipFactory.createAddressFactory();
		headerFactory = sipFactory.createHeaderFactory();

		//	设置监听点
		ListeningPoint lp = sipStack.createListeningPoint(InetAddress.getLocalHost().getHostAddress(), BIND_PORT, TRANSPORT);
		SipProvider sipProvider = sipStack.createSipProvider(lp);
		sipProvider.addSipListener(this);
	}

	@Override
	public void processRequest(RequestEvent e)
	{
		Request request = e.getRequest();
		ServerTransaction serverTransaction = e.getServerTransaction();
		System.out.println("Request " + request.getMethod() + " received at " + sipStack.getStackName() + " with server transaction id " + serverTransaction);

		if (request.getMethod().equals(Request.INVITE))
		{
			try
			{
				processInvite(e, serverTransaction);
			}
			catch (UnknownHostException ex)
			{
				ex.printStackTrace();
			}
			catch (ParseException ex)
			{
				ex.printStackTrace();
			}
			catch (SipException ex)
			{
				ex.printStackTrace();
			}
			catch (InvalidArgumentException ex)
			{
				ex.printStackTrace();
			}
		}
		else if (request.getMethod().equals(Request.ACK))
		{
			try
			{
				processAck(e, serverTransaction);
			}
			catch (SipException ex)
			{
				ex.printStackTrace();
			}
		}
		else if (request.getMethod().equals(Request.BYE))
		{
			try
			{
				processBye(e, serverTransaction);
			}
			catch (ParseException ex)
			{
				ex.printStackTrace();
			}
			catch (SipException ex)
			{
				ex.printStackTrace();
			}
			catch (InvalidArgumentException ex)
			{
				ex.printStackTrace();
			}
		}
	}

	private void processBye(RequestEvent e, ServerTransaction serverTransaction) throws ParseException, SipException, InvalidArgumentException
	{
		System.out.println("BYE received. Send response.");
		Request request = e.getRequest();
		Response response = messageFactory.createResponse(Response.OK, request);
		serverTransaction.sendResponse(response);
	}

	private void processAck(RequestEvent e, ServerTransaction serverTransaction) throws SipException
	{
		System.out.println("ACK received. BYE will be sent, now.");
		SipProvider sipProvider = (SipProvider) e.getSource();
		Dialog dialog = serverTransaction.getDialog();
		Request byeRequest = dialog.createRequest(Request.BYE);
		ClientTransaction clientTransaction = sipProvider.getNewClientTransaction(byeRequest);
		dialog.sendRequest(clientTransaction);
	}

	private void processInvite(RequestEvent e, ServerTransaction serverTransaction) throws ParseException, UnknownHostException, SipException, InvalidArgumentException
	{
		System.out.println("INVITE received.");
		Request request = e.getRequest();
		System.out.println("Request:\n" + request);

		Response response = messageFactory.createResponse(Response.OK, request);
		Address address = addressFactory.createAddress("The SIP Callee <sip:" + InetAddress.getLocalHost().getHostAddress() + ":" + BIND_PORT + ">");
		ContactHeader contactHeader = headerFactory.createContactHeader(address);
		response.addHeader(contactHeader);

		byte[] content = request.getRawContent();
		ContentTypeHeader contentTypeHeader = headerFactory.createContentTypeHeader("application", "sdp");
		response.setContent(content, contentTypeHeader);

		System.out.println("Send response:\n" + response);
		if(serverTransaction == null)
		{
			SipProvider sipProvider = (SipProvider) e.getSource();
			serverTransaction = sipProvider.getNewServerTransaction(request);
		}
		serverTransaction.sendResponse(response);
	}

	@Override
	public void processResponse(ResponseEvent e)
	{
		//	理论上，不应该发生这个事件
		Response response = e.getResponse();
		ClientTransaction clientTransaction = e.getClientTransaction();
		System.out.println("Response received with client transaction id " + clientTransaction + ": " + response);

		CSeqHeader cseqHeader = (CSeqHeader) response.getHeader(CSeqHeader.NAME);
		if(response.getStatusCode() == Response.OK && cseqHeader.getMethod().equals(Request.INVITE))
		{
			Dialog dialog = clientTransaction.getDialog();
			Request request = clientTransaction.getRequest();
			try
			{
				dialog.sendAck(request);
			}
			catch (SipException ex)
			{
				ex.printStackTrace();
			}
		}
	}

	@Override
	public void processTimeout(TimeoutEvent e)
	{
		System.out.println("Transaction time out.");
	}

	@Override
	public void processTransactionTerminated(TransactionTerminatedEvent e)
	{
		System.out.println("Transaction terminated");
	}

	@Override
	public void processDialogTerminated(DialogTerminatedEvent e)
	{
		System.out.println("Dialog terminated");
	}

	@Override
	public void processIOException(IOExceptionEvent e)
	{
		System.out.println("IOException thrown.");
	}

	public static void main(String[] args) throws Exception
	{
		new SipCallee().call();
	}
}
